package dark

import (
	"bytes"
	"io"
)

type NetStr struct {
	Str
	HTTPS bool
}

func (str Str) AsNetReq() NetStr {
	str = str.Trim()
	if str.In("\n") {
		return NetStr{
			Str: str.Replace("\r\n", "\n"),
		}.format()
	} else {
		useHttps := false
		host := Str("")
		uri := Str("")
		if str.StartsWith("http") {
			if str.StartsWith("https") {
				useHttps = true
			}
			hostb := str.Split("://", 2).Last()
			fs := hostb.Split("/", 2)
			host = fs[0]
			if len(fs) > 1 {
				uri = "/" + fs[1]
			} else {
				uri = "/"
			}
		} else {
			fs := str.Split("/", 2)
			host = fs[0]
			if len(fs) > 1 {
				uri = "/" + fs[1]
			} else {
				uri = "/"
			}

		}

		tmp := Str(`GET %s HTTP/1.1
		Host: %s
		Sec-Ch-Ua: "Chromium";v="107", "Not=A?Brand";v="24"
		Sec-Ch-Ua-Mobile: ?0
		Sec-Ch-Ua-Platform: "Windows"
		Upgrade-Insecure-Requests: 1
		User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.41234.63 Safari/537.36
		Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
		Sec-Fetch-Site: none
		Sec-Fetch-Mode: navigate
		Sec-Fetch-User: ?1
		Sec-Fetch-Dest: document
		Accept-Encoding: gzip, deflate
		Accept-Language: zh-CN,zh;q=0.9
		Connection: close
		`).F(uri.Trim(), host.Trim())
		return NetStr{
			Str:   tmp.Replace("\r\n", "\n"),
			HTTPS: useHttps,
		}.format()
	}
}

func (net NetStr) Copy(b Str) NetStr {
	return NetStr{
		Str:   b,
		HTTPS: net.HTTPS,
	}
}

func (net NetStr) SetHead(key, val Str) NetStr {
	key = key.ToFirstUpper().Trim()
	val = val.Trim()
	isHeaderArea := false
	found := false
	newStr := net.Str.TextEveryLineWith(func(lineno int, oldLine Str) Str {
		oldLine = oldLine.Trim()
		if lineno == 0 {
			isHeaderArea = true
			return oldLine
		}
		if oldLine == "" {
			isHeaderArea = false
		}

		if oldLine.StartsWith(key.Str()+": ") && isHeaderArea {
			found = true
			return Str(key.Str() + ": " + val.Str())
		} else {
			return oldLine
		}
	})
	isHeaderArea = false
	if !found {
		newStr = net.Str.TextEveryLineWith(func(lineno int, oldLine Str) Str {
			oldLine = oldLine.Trim()
			if lineno == 0 && key == "Host" {
				isHeaderArea = true
				return oldLine + Str("\n") + key + Str(": ") + val
			}
			if oldLine == "" && isHeaderArea {
				isHeaderArea = false
				return key + Str(": ") + val + "\n"

			} else {
				return oldLine
			}
		})
	}
	return net.Copy(newStr)
}

func (net NetStr) GetHead(key Str) (val Str) {
	key = key.ToFirstUpper()
	isHeaderArea := false
	net.Str.EveryLine(func(lineno int, oldLine Str) {
		oldLine = oldLine.Trim()
		if lineno == 0 {
			isHeaderArea = true
			return
		}
		if isHeaderArea && oldLine == "" {
			isHeaderArea = false
		} else if isHeaderArea && oldLine.StartsWith(key.Str()+": ") {
			val = oldLine.Split(": ", 2).Last().Trim()
		}
	})
	return
}

func (net NetStr) format() NetStr {
	ifBodyExist := false
	isNotHeader := true
	newStr := net.Str.TextEveryLineWith(func(lineno int, oldLine Str) Str {
		oldLine = oldLine.Trim()
		if lineno == 0 {
			isNotHeader = false
			return oldLine
		}
		if oldLine == "" && !isNotHeader {
			isNotHeader = true
			return oldLine
		}
		if isNotHeader && oldLine != "" {
			ifBodyExist = true
		}
		return oldLine
	})
	if !ifBodyExist && !isNotHeader {
		newStr = newStr.Trim() + Str("\n\n")
	}
	return net.Copy(newStr)
}

func (net NetStr) DelHead(key Str) NetStr {
	key = key.Trim().ToFirstUpper()
	isHeaderArea := false
	return net.Copy(net.Str.TextEveryLineWith(func(lineno int, oldLine Str) Str {
		oldLine = oldLine.Trim()
		if lineno == 0 {
			isHeaderArea = true
			return oldLine
		}
		if isHeaderArea && oldLine.StartsWith(key.Str()) {
			return ""
		} else {
			return oldLine
		}
	}))

}

func (net NetStr) Header() (keys Dict[Str]) {
	keys = make(Dict[Str])
	isHeaderArea := false
	net.Str.EveryLine(func(lineno int, oldLine Str) {
		oldLine = oldLine.Trim()
		if lineno == 0 {
			isHeaderArea = true
			return
		}
		if oldLine == "" && isHeaderArea {
			isHeaderArea = false
			return
		}
		if isHeaderArea && oldLine.In(":") {
			linekv := oldLine.ParseKV(true)
			keys = keys.Update(linekv)
		}
	})
	return
}

func (net NetStr) S() Str {
	return net.Str
}

func (net NetStr) Body() (body Str) {
	if net.In("\n\n") {
		return net.Split("\n\n", 2).Last()
	} else {
		return ""
	}
}

func (net NetStr) SetBody(body Str) NetStr {
	cc := net.Split("\n\n", 2).Nth(0).Trim() + "\n\n" + body
	return net.Copy(cc)

}

func (net NetStr) SetForm(key, val Str) NetStr {
	method := net.Method()
	if method == "POST" {
		body := net.Body()

		kv := body.ParseUri()
		kv[key.Trim().Str()] = val
		newbody := kv.AsURI()
		return net.SetBody(newbody)

	} else if method == "GET" {
		uri := net.Uri()
		kv := uri.Split("?", 2).Last().ParseUri()
		kv[key.Trim().Str()] = val
		newuri := uri.Split("?", 2).Nth(0).Add("?") + kv.AsURI()
		return net.SetUri(newuri)
	} else {
		return net
	}
}

func (net NetStr) BodyJson() (jbody Dict[any]) {
	body := net.Body()
	if body.StartsWith("{") && body.EndsWith("}") {
		return body.Json()
	}
	return
}

func (net NetStr) Uri() Str {
	l := Str("")
	net.EveryLine(func(lineno int, line Str) {
		if lineno == 0 {
			l = line.Split(" ", 2).Last().Split(" HTTP/1", 2).Nth(0)
		}
	})
	return l.Trim()
}

func (net NetStr) Host() Str {
	return net.GetHead("host")
}

func (net NetStr) URL() Str {
	host := net.Host()
	uri := net.Uri()
	return host + uri
}

func (net NetStr) BodyReader() io.Reader {
	return bytes.NewBuffer(net.Body().Bytes())
}

func (net NetStr) Method() Str {
	l := Str("")
	net.EveryLine(func(lineno int, line Str) {
		if lineno == 0 {
			l = line.Split(" ", 2).Nth(0)
		}
	})
	return l.Trim()
}

func (net NetStr) SetMethod(method Str) NetStr {
	method = method.Upper().Trim()
	return net.Copy(net.TextEveryLineWith(func(lineno int, line Str) Str {
		line = line.Trim()
		if lineno == 0 {
			fs := line.Split(" ", 2)
			return method + " " + fs.Last()
		}
		return line
	}))
}

func (net NetStr) GetHeaderDict(key Str) (vals Dict[Str]) {
	vals = make(Dict[Str])
	key = key.ToFirstUpper().Trim()
	isHeaderArea := false
	net.Str.EveryLine(func(lineno int, oldLine Str) {
		oldLine = oldLine.Trim()
		if lineno == 0 {
			isHeaderArea = true
			return
		}
		if oldLine == "" && isHeaderArea {
			isHeaderArea = false
			return
		}
		if isHeaderArea && oldLine.StartsWith(key.Add(": ").Str()) {
			for k, v := range oldLine.Split(key.Add(": ").Str(), 2).Last().ParseKV() {
				vals[k] = v
			}

		}
	})
	return
}

func (net NetStr) Cookie() (cookies Dict[Str]) {
	return net.GetHeaderDict("cookie")
}

func (net NetStr) SetCookie(key, val Str) NetStr {
	kvs := net.Cookie()
	kvs[key.Trim().Str()] = val.Trim()
	cookies := kvs.Format("=", ";")
	return net.SetHead("cookie", cookies)
}

func (net NetStr) SetUri(uri Str) NetStr {
	if !uri.StartsWith("/") {
		uri = net.Uri().Add("/") + uri
	}
	return net.Copy(net.TextEveryLineWith(func(lineno int, line Str) Str {
		line = line.Trim()
		if lineno == 0 {
			fs := line.Split(" ", 2)
			httpVer := fs.Last().Split(" HTTP/", 2).Last()
			method := fs.Nth(0)
			return method + " " + uri + " HTTP/" + httpVer
		}
		return line
	}))
}

func (net NetStr) Stand() Str {
	return net.Replace("\n", "\r\n")
}
